<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="yVm9D" id="yVm9D"><span data-lake-id="uba5a0545" id="uba5a0545">典型回答</span></h1>
  <p data-lake-id="u8ffba753" id="u8ffba753"><br></p>
  <p data-lake-id="u2de1eaa7" id="u2de1eaa7"><span data-lake-id="ub7c93a01" id="ub7c93a01">柔性事务，是业内解决分布式事务的主要方案。所谓柔性事务，相比较与数据库事务中的ACID这种刚性事务来说，柔性事务保证的是“基本可用，最终一致。”这其实就是基于BASE理论，保证数据的最终一致性。</span></p>
  <p data-lake-id="ud0496fc0" id="ud0496fc0"><span data-lake-id="u05cff0ce" id="u05cff0ce">​</span><br></p>
  <p data-lake-id="u71b062f0" id="u71b062f0"><span data-lake-id="u45c5aa68" id="u45c5aa68">虽然柔性事务并不像刚性事务那样完全遵循ACID，但是，也是部分遵循ACID的，简单看一下关于ACID四个属性，柔性事务的支撑程度：</span></p>
  <p data-lake-id="u286986df" id="u286986df"><br></p>
  <blockquote data-lake-id="u26d77c1f" id="u26d77c1f">
   <p data-lake-id="u30932f51" id="u30932f51"><span data-lake-id="ufb58209b" id="ufb58209b">原子性：严格遵循</span></p>
   <p data-lake-id="uf2bbfd35" id="uf2bbfd35"><span data-lake-id="u8a27e1f1" id="u8a27e1f1"> </span></p>
   <p data-lake-id="u04047df8" id="u04047df8"><span data-lake-id="ud595e399" id="ud595e399">一致性：事务完成后的一致性严格遵循；事务中的一致性可适当放宽</span></p>
   <p data-lake-id="udf792c68" id="udf792c68"><span data-lake-id="u7237a0f3" id="u7237a0f3"> </span></p>
   <p data-lake-id="u92f732fb" id="u92f732fb"><span data-lake-id="u09376060" id="u09376060">隔离性：并行事务间不可影响；事务中间结果可见性允许安全放宽</span></p>
   <p data-lake-id="u18f0a73a" id="u18f0a73a"><span data-lake-id="uc8e2ee55" id="uc8e2ee55"> </span></p>
   <p data-lake-id="u2fc9f7ce" id="u2fc9f7ce"><span data-lake-id="u8caa7eec" id="u8caa7eec">持久性：严格遵循</span></p>
  </blockquote>
  <p data-lake-id="u583fbe58" id="u583fbe58"><br></p>
  <p data-lake-id="u5538a868" id="u5538a868"><span data-lake-id="ub587752b" id="ub587752b">在业内，关于柔性事务</span><strong><span data-lake-id="u81304675" id="u81304675">，最主要的有以下三种类型：异步确保型、补偿型、最大努力通知型。</span></strong></p>
  <p data-lake-id="u92e5fce2" id="u92e5fce2"><br></p>
  <h1 data-lake-id="oMaLA" id="oMaLA"><span data-lake-id="u44acf86f" id="u44acf86f">扩展知识</span></h1>
  <p data-lake-id="u8663f8ec" id="u8663f8ec"><br></p>
  <p data-lake-id="ucde15327" id="ucde15327"><span data-lake-id="u6830f962" id="u6830f962">想要实现柔性事务，有几个基础条件需要具备，以下介绍几个柔性事务实现的基础。</span></p>
  <h2 data-lake-id="jABXR" id="jABXR"><span data-lake-id="u2adf6600" id="u2adf6600">可查询操作</span></h2>
  <p data-lake-id="u41270f96" id="u41270f96"><span data-lake-id="u2544ca11" id="u2544ca11">​</span><br></p>
  <p data-lake-id="u827ca462" id="u827ca462"><span data-lake-id="u928ec7c6" id="u928ec7c6">可查询操作，几乎是所有的分布式解决方案都需要的。</span></p>
  <p data-lake-id="ufb89249e" id="ufb89249e"><span data-lake-id="uf2f63973" id="uf2f63973">​</span><br></p>
  <p data-lake-id="ud82c4a79" id="ud82c4a79"><span data-lake-id="ubd810d13" id="ubd810d13">举一个常见的分布式场景的例子，如订单处理这一功能：</span></p>
  <p data-lake-id="u38455750" id="u38455750"><span data-lake-id="ued2d8b8e" id="ued2d8b8e">​</span><br></p>
  <p data-lake-id="u1c36132a" id="u1c36132a"><span data-lake-id="u2347430a" id="u2347430a"> </span></p>
  <pre lang="java"><code>
/** 支付订单处理 **/
public void completeOrder() {
    orderDao.update(); // 订单服务本地更新订单状态
    accountService.update(); // 调用资金账户服务给资金帐户加款
    pointService.update(); // 调用积分服务给积分帐户增加积分
    accountingService.insert(); // 调用会计服务向会计系统写入会计原始凭证
    merchantNotifyService.notify(); // 调用商户通知服务向商户发送支付结果通知
}
</code></pre>
  <p data-lake-id="u6867997a" id="u6867997a"><span data-lake-id="u4a11e2c0" id="u4a11e2c0"> </span></p>
  <p data-lake-id="uce709c31" id="uce709c31"><span data-lake-id="uc653c86f" id="uc653c86f">以上这个支付订单处理的例子中，除了</span><code data-lake-id="u9a661877" id="u9a661877"><span data-lake-id="u10a4d56d" id="u10a4d56d">订单服务本地更新订单状态</span></code><span data-lake-id="u549c6b3a" id="u549c6b3a">以外的所有操作，都需要调用RPC接口来执行，这种情况单纯的本地事务就无法保证数据的一致性了。就需要引入分布式事务。</span></p>
  <p data-lake-id="u1e30c365" id="u1e30c365"><span data-lake-id="ufeafb546" id="ufeafb546">​</span><br></p>
  <p data-lake-id="ue6ad4f6f" id="ue6ad4f6f"><span data-lake-id="u72653d27" id="u72653d27">在分布式事务执行过程中，如果某一个步骤执行出错，就需要明确的知道其他几个操作的处理情况，这就需要其他的服务都能够提供查询接口，保证可以通过查询来判断操作的处理情况。</span></p>
  <p data-lake-id="uc0f68b6d" id="uc0f68b6d"><span data-lake-id="u2a81bdfe" id="u2a81bdfe">​</span><br></p>
  <p data-lake-id="u2f7b807d" id="u2f7b807d"><img src="https://cdn.nlark.com/yuque/0/2023/png/5378072/1676785247749-a617e323-d682-4086-a8de-d8a70ef7a7f6.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_12%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="ucff84b96" id="ucff84b96"><span data-lake-id="u72dd8b82" id="u72dd8b82">​</span><br></p>
  <p data-lake-id="u67111863" id="u67111863"><span data-lake-id="ua0c3a9dd" id="ua0c3a9dd">为了保证操作的可查询，需要对于每一个服务的每一次调用都有一个全局唯一的标识，可以是业务单据号（如订单号）、也可以是系统分配的操作流水号（如支付记录流水号）。除此之外，操作的时间信息也要有完整的记录。</span></p>
  <p data-lake-id="u5a5c67d2" id="u5a5c67d2"><span data-lake-id="u54c41b0d" id="u54c41b0d">​</span><br></p>
  <h2 data-lake-id="hKbw2" id="hKbw2"><span data-lake-id="ud4624c54" id="ud4624c54">幂等操作</span></h2>
  <p data-lake-id="u59d285f3" id="u59d285f3"><span data-lake-id="u3b8f5b69" id="u3b8f5b69">​</span><br></p>
  <p data-lake-id="u1e14040b" id="u1e14040b"><span data-lake-id="ue7ae02bd" id="ue7ae02bd">幂等性，其实是一个数学概念。幂等函数，或幂等方法，是指可以使用相同参数重复执行，并能获得相同结果的函数，如：</span></p>
  <p data-lake-id="u74aad60d" id="u74aad60d"><span data-lake-id="u6c9761e6" id="u6c9761e6">​</span><br></p>
  <p data-lake-id="u91d02614" id="u91d02614"><span data-lake-id="u07b5b61b" id="u07b5b61b"> f(f(x)) = f(x)</span></p>
  <p data-lake-id="uffe169c1" id="uffe169c1"><span data-lake-id="u5f4a82d7" id="u5f4a82d7"> </span></p>
  <p data-lake-id="u0030ae59" id="u0030ae59"><span data-lake-id="ue9aad804" id="ue9aad804">​</span><br></p>
  <p data-lake-id="uc550dcae" id="uc550dcae"><span data-lake-id="ub80201c5" id="ub80201c5">在编程中一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。也就是说，同一个方法，使用同样的参数，调用多次产生的业务结果与调用一次产生的业务结果相同。</span></p>
  <p data-lake-id="ua49c23ff" id="ua49c23ff"><span data-lake-id="u3f882c11" id="u3f882c11">​</span><br></p>
  <p data-lake-id="u521887e1" id="u521887e1"><img src="https://cdn.nlark.com/yuque/0/2023/png/5378072/1676785273318-04757d3e-a013-456e-adf0-865bd75e2e1b.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_12%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="u0a771dff" id="u0a771dff"><span data-lake-id="u9fb8439d" id="u9fb8439d">​</span><br></p>
  <p data-lake-id="u84c25bee" id="u84c25bee"><span data-lake-id="u6a1616bc" id="u6a1616bc">这一个要求其实也比较好理解，因为要保证数据的最终一致性，很多解决防范都会有很多重试的操作，如果一个方法不保证幂等，那么将无法被重试。</span></p>
  <p data-lake-id="u52d7dff8" id="u52d7dff8"><span data-lake-id="u049b53d9" id="u049b53d9">​</span><br></p>
  <p data-lake-id="ue8411c46" id="ue8411c46"><span data-lake-id="ue5e41a56" id="ue5e41a56">幂等操作的实现方式有多种，如在系统中缓存所有的请求与处理结果、检测到重复操作后，直接返回上一次的处理结果等。</span></p>
  <p data-lake-id="uaee4fc18" id="uaee4fc18"><span data-lake-id="ubf04ebfd" id="ubf04ebfd">​</span><br></p>
  <h2 data-lake-id="mSnCs" id="mSnCs"><span data-lake-id="u98383614" id="u98383614">可补偿操作</span></h2>
  <p data-lake-id="uc480e276" id="uc480e276"><span data-lake-id="u21c92df0" id="u21c92df0">​</span><br></p>
  <p data-lake-id="ua6b005cc" id="ua6b005cc"><span data-lake-id="u0fbab5a4" id="u0fbab5a4">提到事务，为了保证原子性，就可能发生commit和rollback，那么在分布式事务中，要想进行rollback，就需要提供可补偿操作。</span></p>
  <p data-lake-id="u9b9be725" id="u9b9be725"><span data-lake-id="u97229fa6" id="u97229fa6">​</span><br></p>
  <p data-lake-id="u18b1839d" id="u18b1839d"><img src="https://cdn.nlark.com/yuque/0/2023/png/5378072/1676785294019-870c530a-8c74-480e-9d59-517e0d0b29d0.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_11%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="u09ca71e5" id="u09ca71e5"><span data-lake-id="uc86fd597" id="uc86fd597">​</span><br></p>
  <p data-lake-id="u0dfa40aa" id="u0dfa40aa"><span data-lake-id="u5c685499" id="u5c685499">比如上面的订单处理的例子中，在`调用积分服务给积分帐户增加积分`操作执行之后，经过分布式事务协调，最终决定回滚整个事务，那么就需要提供一个`调用积分服务给积分帐户扣减积分`的操作。</span></p>
  <p data-lake-id="u655e8dca" id="u655e8dca"><span data-lake-id="ubebde66a" id="ubebde66a">​</span><br></p>
  <p data-lake-id="ua3d49b3f" id="ua3d49b3f"><span data-lake-id="uf2774209" id="uf2774209">并且，补偿操作同时也需要满足幂等性。</span></p>
  <p data-lake-id="uf4561d32" id="uf4561d32"><span data-lake-id="u56553acd" id="u56553acd">​</span><br></p>
  <h2 data-lake-id="IkBJ3" id="IkBJ3"><span data-lake-id="ue9ccd3fb" id="ue9ccd3fb">TCC操作</span></h2>
  <p data-lake-id="u230cc8e3" id="u230cc8e3"><span data-lake-id="ue642b64d" id="ue642b64d">​</span><br></p>
  <p data-lake-id="ud6e81c8f" id="ud6e81c8f"><span data-lake-id="u19a3a7a7" id="u19a3a7a7">TCC 即 Try-Confirm-Cancel。</span></p>
  <p data-lake-id="u5c536bed" id="u5c536bed"><span data-lake-id="u6c1e5f19" id="u6c1e5f19">​</span><br></p>
  <p data-lake-id="u790a2d5c" id="u790a2d5c"><img src="https://cdn.nlark.com/yuque/0/2023/png/5378072/1676785315031-87e9c3e1-c3db-4409-bade-85f42dbc2048.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_11%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="u9f621898" id="u9f621898"><span data-lake-id="ufed03d18" id="ufed03d18">​</span><br></p>
  <p data-lake-id="u53ee5d88" id="u53ee5d88"><strong><span data-lake-id="u8dbafe94" id="u8dbafe94">Try: 尝试执行业务</span></strong><span data-lake-id="u83915f1a" id="u83915f1a">​</span></p>
  <p data-lake-id="u8616251e" id="u8616251e"><span data-lake-id="uc5d4454d" id="uc5d4454d">​</span><br></p>
  <p data-lake-id="u1542f62e" id="u1542f62e"><span data-lake-id="uf61ff186" id="uf61ff186">完成所有业务检查(一致性) 预留必须业务资源(准隔离性)</span></p>
  <p data-lake-id="uf113d143" id="uf113d143"><span data-lake-id="ud597b060" id="ud597b060">​</span><br></p>
  <p data-lake-id="ucdaef863" id="ucdaef863"><strong><span data-lake-id="u96e10f49" id="u96e10f49">Confirm:确认执行业务</span></strong><span data-lake-id="u35586b95" id="u35586b95">​</span></p>
  <p data-lake-id="ud3ff51f2" id="ud3ff51f2"><span data-lake-id="ue3f1c83b" id="ue3f1c83b">​</span><br></p>
  <p data-lake-id="u635fa328" id="u635fa328"><span data-lake-id="u83032f76" id="u83032f76">真正执行业务 不作任何业务检查 只使用Try阶段预留的业务资源 Confirm操作要满足幂等性</span></p>
  <p data-lake-id="u03ad08bf" id="u03ad08bf"><span data-lake-id="u550ab861" id="u550ab861">​</span><br></p>
  <p data-lake-id="ud2e59608" id="ud2e59608"><strong><span data-lake-id="ud7bbbc1f" id="ud7bbbc1f">Cancel: 取消执行业务</span></strong><span data-lake-id="u140fc0bb" id="u140fc0bb">​</span></p>
  <p data-lake-id="u5104bf92" id="u5104bf92"><span data-lake-id="u3605880e" id="u3605880e">​</span><br></p>
  <p data-lake-id="u953c07a9" id="u953c07a9"><span data-lake-id="u03bfdd3f" id="u03bfdd3f">释放Try阶段预留的业务资源 ，Cancel操作要满足幂等性</span></p>
  <p data-lake-id="ub5fcae7e" id="ub5fcae7e"><span data-lake-id="uc561ba0d" id="uc561ba0d">​</span><br></p>
  <p data-lake-id="ua8c7bb59" id="ua8c7bb59"><span data-lake-id="u843ebeee" id="u843ebeee">这种类型和可补偿操作类似，就是提供一种提交和回滚的机制。是一种典型的两阶段类型的操作。这里说的两阶段类型操作并不是指2PC，他和2PC还是有区别的。</span></p>
  <p data-lake-id="u58b85f4b" id="u58b85f4b"><br></p>
 </body>
</html>